package com.lzc.jedis.controller;

import com.lzc.jedis.util.RedisPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class TestController {

    public static final String LOCK_PRODUCT_ID_KEY = "lockProductIdKey_"; // 该key用来锁住商品id
    public static final String PRODUCT_ID_KEY = "productIdKey_"; // 该key用来存放商品信息

    /**
     * 模拟购买商品
     * id为i的商品库存量使用productIdKey_i存储
     */

    /**
     *
     * @param productId 商品id
     * @param num 购买数量
     * @return
     */
    @GetMapping("/buyProduct/{productId}/{num}")
    public Object buyProduct(@PathVariable String productId,@PathVariable Integer num) {
        // 先判断该商品是否存在
        if (!RedisPoolUtil.exists(PRODUCT_ID_KEY + productId)) {
            return "商品不存在";
        }

        Long lockTimeout = 5000L;

        Long setnxResult = RedisPoolUtil.setnx(LOCK_PRODUCT_ID_KEY + productId, String.valueOf(System.currentTimeMillis() + lockTimeout));
        if (setnxResult != null && setnxResult.intValue() == 1) {
            // 获取到锁
            try {
                doSomething(productId, num);
            } catch (Exception e) {
                return e.getMessage();
            }
        } else {
            // 没有获取到锁，继续判断，看是否能否再次获取到锁
            String lockValue = RedisPoolUtil.get(LOCK_PRODUCT_ID_KEY + productId);
            // 判断是否过期
            if(lockValue != null && System.currentTimeMillis() > Long.valueOf(lockValue)) {
                // 当锁过期时，进行getSet操作，再判断getsetResult的结果
                // 如果 getsetResult 为 null，说明锁已经被释放，可以获取锁
                // getsetResult 与 lockValue 相等，说明值没有被其他线程修改，可以获取锁
                String getsetResult = RedisPoolUtil.getSet(LOCK_PRODUCT_ID_KEY + productId, String.valueOf(System.currentTimeMillis() + lockTimeout));
                if (getsetResult == null || getsetResult.equals(lockValue)) {
                    // 获取到锁
                    try {
                        doSomething(productId, num);
                    } catch (Exception e) {
                        return e.getMessage();
                    }
                } else {
                    log.info("没有获取到分布式锁:{}",LOCK_PRODUCT_ID_KEY + productId);
                    return "太拥挤了，请稍后再重试";
                }
            } else {
                log.info("没有获取到分布式锁:{}",LOCK_PRODUCT_ID_KEY + productId);
                return "太拥挤了，请稍后再重试";
            }
        }
        return "购买成功";
    }

    private void doSomething(String productId, Integer num) throws Exception {
        // 1获取到锁后加一个过期时间，这一步也可以不设置
        RedisPoolUtil.expire(productId, 60*5); // 设置5分钟，防止死锁
        log.info("\n");
        log.info("获取{},ThreadName:{}",PRODUCT_ID_KEY + productId,Thread.currentThread().getName());

        // 进行相关操作
        // 获取产品数量
        Integer getNum = Integer.valueOf(RedisPoolUtil.get(PRODUCT_ID_KEY + productId));
        if (getNum < num) {
            unlock(LOCK_PRODUCT_ID_KEY + productId);
            throw new Exception("商品库存不足");
        }

        RedisPoolUtil.set(PRODUCT_ID_KEY + productId, String.valueOf(getNum - num)); // 减库存

        // 删除锁
        unlock(LOCK_PRODUCT_ID_KEY + productId);
    }

    private void unlock(String key) {
        RedisPoolUtil.del(key);
        log.info("释放{},ThreadName:{}",key,Thread.currentThread().getName());
        log.info("===============================\n");
    }


}
